﻿using System;
using Mono.Cecil;
using SPCAF.Sdk;
using SPCAF.Sdk.Model;
using SPCAFContrib.Entities.Consts;
using SPCAFContrib.Extensions;
using SPCAFContrib.Groups;
using SPCAFContrib.Rules.Code.Base;

namespace SPCAFContrib.Rules.Code
{
    [RuleMetadata(typeof(ContribBestPracticesGroup),
     CheckId = CheckIDs.Rules.Assembly.PutSPFileExistsIntoTryCatchBlock,
     Help = CheckIDs.Rules.Assembly.PutSPFileExistsIntoTryCatchBlock_HelpUrl,

     Message = "Put SPFile.Exists into try ... catch block. Method: [{0}], Class:[{1}]",
     DisplayName = "Put SPFile.Exists into try ... catch block.",
     Description = "If a file doesn’t exist, it throws an ArgumentException error.",
     Resolution = "Surround SPFile.Exists into try ... catch block.",

     DefaultSeverity = Severity.Warning,
     SharePointVersion = new[] { "12", "14", "15" },
     Links = new[]
     {
         "SPFile.Exists Property",
         "http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.spfile.exists.aspx"
     }
     )]
    public class PutSPFileExistsIntoTryCatchBlock : SearchPropertyRuleBase  
    {
        protected override void PopulateTypeMap()
        {
            TargetTypeMap.Add(TypeKeys.SPFile, "Exists");
        }

        public override void Visit(AssemblyFileReferenceWSP assembly, NotificationCollection notifications)
        {
            if (assembly.AssemblyFileReference.AssemblyDefinition == null)
                assembly.AssemblyFileReference.AssemblyDefinition = AssemblyDefinition.ReadAssembly(assembly.AssemblyLocation);

            if (assembly.AssemblyHasExcluded()) return;

            EnsureTypeMap();

            assembly.OnPropertyUsageMatch(TargetTypeMap, (_instruction) =>
            {
                Mono.Cecil.MethodDefinition method = _instruction.MethodDefinition;

                if (method.FindInnermostExceptionHandler(_instruction.Instruction.Offset) == null)
                {
                    this.OnMatch(assembly, _instruction, notifications,
                        () =>
                        {
                            return String.Format(this.MessageTemplate(), method.Name, method.DeclaringType.FullName);
                        },
                        () =>
                        {
                            return GetSummary(assembly, _instruction);
                        });
                }
            });
        }
    }
}
